//*****************************************************************************
//
// packet_handler.c - This file implements the packet handler functions used
//                    by the boot loader.
//
// Copyright (c) 2006-2007 Luminary Micro, Inc.  All rights reserved.
//
// Software License Agreement
//
// Luminary Micro, Inc. (LMI) is supplying this software for use solely and
// exclusively on LMI's microcontroller products.
//
// The software is owned by LMI and/or its suppliers, and is protected under
// applicable copyright laws.  All rights are reserved.  Any use in violation
// of the foregoing restrictions may subject the user to criminal sanctions
// under applicable laws, as well as to civil liability for the breach of the
// terms and conditions of this license.
//
// THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
// OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
// LMI SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR
// CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
//
// This is part of revision 323 of the Stellaris boot loader.
//
//*****************************************************************************

#include "boot_loader.h"

//*****************************************************************************
//
//! \defgroup packet_handler_src Packet Handler APIs
//! This section describes the functions that are responsible for handling the
//! packets in the format that is supported by the boot loader.
//! These functions call the SendData() and ReceiveData() functions to handle
//! the actual transmission of bytes.
//! @{
//
//*****************************************************************************

unsigned char CheckSum(unsigned char const *pucData,
                       unsigned char ucSize);

//*****************************************************************************
//
//! This is the value that is sent to acknowledge a packet.
//
//*****************************************************************************
#define COMMAND_ACK                 0xcc

//*****************************************************************************
//
//! This is the value that is sent to not-acknowledge a packet.
//
//*****************************************************************************
#define COMMAND_NAK                 0x33

//*****************************************************************************
//
//! Receives a data packet.
//!
//! \param pucData is the location to store the data that is sent to the
//!     boot loader.
//! \param pucSize is the number of bytes returned in the pucData buffer that
//!     was provided.
//!
//! This function receives a packet of data from specified transfer function by
//! calling the global function pointer, ReceiveData().  This should have been
//! set to a known function before attempting to send or receive any packets.
//!
//! \returns The function returns zero to indicate success while any non-zero
//!     value indicates a failure.
//
//*****************************************************************************
int
ReceivePacket(unsigned char *pucData, unsigned char *pucSize)
{
    unsigned char ucCheckSum;
    unsigned char ucSize;

    //
    // Wait for non-zero data before getting the first byte that holds the
    // size of the packet we are receiving.
    //
    do
    {
        ReceiveData(&ucSize, 1);
    }
    while(ucSize == 0);

    //
    // Subtract off the size and checksum bytes.
    //
    ucSize -= 2;

    //
    // Receive the checksum followed by the actual data.
    //
    ReceiveData(&ucCheckSum, 1);

    //
    // If there is room in the buffer then receive the requested data.
    //
    if(*pucSize >= ucSize)
    {
        //
        // Receive the actual data in the packet.
        //
        ReceiveData(pucData, ucSize);

        //
        // Send a no acknowledge if the checksum does not match, otherwise send
        // an acknowledge to the packet later.
        //
        if(CheckSum(pucData, ucSize) != ucCheckSum)
        {
            //
            // Indicate tha the packet was not received correctly.
            //
            NakPacket();

            //
            // Packet was not received, there is no valid data in the buffer.
            //
            return(-1);
        }
    }
    else
    {
        //
        // If the caller allocated a buffer that was too small for the received
        // data packet, receive it but don't fill the buffer.
        // Then inform the caller that the packet was not received correctly.
        //
        ReceiveData(0, ucSize);

        //
        // Packet was not received, there is no valid data in the buffer.
        //
        return(-1);
    }

    //
    // Make sure to return the number of bytes received.
    //
    *pucSize = ucSize;

    //
    // Packet was received successfully.
    //
    return(0);
}

//*****************************************************************************
//
//! Sends a No Acknowledge packet.
//!
//! When a packet is received with some form of error, this function is called
//! to send a no acknowledge.  This is called by ReceivePacket() and is
//! normally not called outside the packet handling functions, however, this
//! function is provided in the event that a custom interface requires this
//! functionality.
//!
//! \return The function returns zero to indicate success while any non-zero
//!     value indicates a failure.
//
//*****************************************************************************
void
NakPacket(void)
{
    const unsigned char ucNAK[2] = { 0, COMMAND_NAK };

    //
    // ACK/NAK packets are the only ones with no size.
    //
    SendData(ucNAK, 2);
}

//*****************************************************************************
//
//! Sends an Acknowledge packet.
//!
//! This function is called to acknowledge that a packet has been received by
//! the microcontroller.
//!
//! \return The function returns zero to indicate success while any non-zero
//!     value indicates a failure.
//
//*****************************************************************************
void
AckPacket(void)
{
    const unsigned char ucACK[2] = { 0, COMMAND_ACK };

    //
    // ACK/NAK packets are the only ones with no size.
    //
    SendData(ucACK, 2);
}

//*****************************************************************************
//
//! Sends a data packet.
//!
//! \param pucData is the location of the data to be sent to the
//!     microcontroller.
//! \param ucSize is the number of bytes to send.
//!
//! This function sends the data provided in the pucData parameter in the
//! packet format used by the boot loader.  The caller only needs to specify
//! the buffer with the data that needs to be transferred.  This call
//! addresses all other packet formatting issues.
//!
//! \return The function returns zero to indicate success while any non-zero
//!     value indicates a failure.
//
//*****************************************************************************
int
SendPacket(unsigned char *pucData, unsigned char ucSize)
{
    unsigned char ucChecksum;
    unsigned char ucAck;

    //
    // Caculate the checksum to be sent out with the data.
    //
    ucChecksum = CheckSum(pucData, ucSize);

    //
    // Need to include the size and checksum bytes in the packet.
    //
    ucSize += 2;

    //
    // Send out the size followed by the data.
    //
    SendData(&ucSize, 1);
    SendData(&ucChecksum, 1);
    SendData(pucData, ucSize - 2);

    //
    // Wait for a non zero byte.
    //
    do
    {
        ReceiveData(&ucAck, 1);
    }
    while(ucAck == 0);

    //
    // Check if the byte was a valid ACK and return a negative value if it was
    // not and aknowledge.
    //
    if(ucAck != COMMAND_ACK)
    {
        return(-1);
    }

    //
    // This packet was sent and received successfully.
    //
    return(0);
}

//*****************************************************************************
//
//! Calculates an 8 bit checksum
//!
//! \param pucData is a pointer to an array of 8 bit data of size ucSize.
//! \param ucSize is the size of the array that will run through the checksum
//!     algorithm.
//!
//! This function simply calculates an 8 bit checksum on the data passed in.
//!
//! \return The function returns the calculated checksum.
//
//*****************************************************************************
unsigned char
CheckSum(unsigned char const *pucData, unsigned char ucSize)
{
    int iOffset;
    unsigned char ucCheckSum;

    //
    // Initialize the checksum to zero.
    //
    ucCheckSum = 0;

    //
    // Add up all the bytes, do not do anything for an overflow.
    //
    for(iOffset = 0; iOffset < ucSize; ++iOffset)
    {
        ucCheckSum += pucData[iOffset];
    }

    //
    // Return the caculated check sum.
    //
    return(ucCheckSum);
}

//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************
